昨天我們看過了預設程式碼內 routing() 的實作
routing {
get("/") {
call.respondText("Hello World!")
}
}
看完 routing() 的實作之後,下一步我們來看 get() 函數的實作。
get() 函數可以在 Ktor 框架內幫我們定義一個接收 GET 請求的路由,實作如下
/**
* Builds a route to match `GET` requests with the specified [path].
* @see [Application.routing]
*/
@KtorDsl
public fun RoutingBuilder.get(path: String, body: RoutingHandler): RoutingBuilder {
return route(path, HttpMethod.Get) { handle(body) }
}
粗略地看,可以看出這邊做的事情就是使用 route 函數,搭配 HttpMethod.Get,來建立一個 GET 的路由。
不過細心的讀者可能會發現:body 應該要是一個函數,才能允許程式內使用 {} 定義內容。這邊的寫法 RoutingHandler,好像是一個介面或者類別一樣?
我們來看看 RoutingHandler 的實作:
public typealias RoutingHandler = suspend RoutingContext.() -> Unit
我們會發現如前面所說,body 確實是一個函數沒錯,不過為了讓程式看起來更加好懂,這邊利用了 Kotlin 的 typealias 關鍵字,將 RoutingHandler 定義成了 suspend RoutingContext.() -> Unit 的同義詞。
另外我們也會發現,這個函數標記了 suspend 關鍵字,所以這會是一個可以用 Coroutine 暫停的函數。
下一段,我們來看 route() 的實作
@KtorDsl
public fun RoutingBuilder.route(path: String, method: HttpMethod, build: RoutingBuilder.() -> Unit): RoutingBuilder {
val selector = HttpMethodRouteSelector(method)
return createRouteFromPath(path).createChild(selector).apply(build)
}
根據 HttpMethodRouteSelector 的命名,我們可以猜這是用來篩選請求是否跟設定的 HTTP Method 一致。
我們來看看,實際上是不是我們所想的這樣:
public data class HttpMethodRouteSelector(
val method: HttpMethod
) : RouteSelector() {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation {
if (context.call.request.httpMethod == method) {
return RouteSelectorEvaluation.Constant
}
return RouteSelectorEvaluation.FailedMethod
}
override fun toString(): String = "(method:${method.value})"
}
可能有點出乎意料,HttpMethodRouteSelector 竟然是一個 data class!這邊利用了 data class 幫忙實作 setter 的部分,所以我們只需要實作判斷的 evaluate()。如果請求的 method 和設置的 method 相同,這邊會回傳 RouteSelectorEvaluation.Constant,定義如下:
/**
* Route evaluation succeeded for a constant value.
*/
public val Constant: RouteSelectorEvaluation = RouteSelectorEvaluation.Success(RouteSelectorEvaluation.qualityConstant)
如果比對不相同,則改回傳 RouteSelectorEvaluation.FailedMethod。
看過了 HttpMethodRouteSelector,我們明天再來看看
return createRouteFromPath(path).createChild(selector).apply(build)
這一段程式碼,是怎麼處理我們輸入的路徑的